﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace exercice1
	{
	public class Hour
		{

		/*---------------------------------------------*\
		|*                 Attributs                   *|
		\*---------------------------------------------*/
		private Int64 m_h;
		private Int64 m_m;
		private Int64 m_s;
		private readonly int m_hashcode; //Numéro unique par instance

		/*---------------------*\
		|*        static       *|
		\*---------------------*/
		static private int m_cptClassInstance = 0; //utilisé pour donner un nombre unique a getHashCode(). Chaque instance possède un numéro unique.

		/*---------------------------------------------*\
		|*                Constructeur                 *|
		\*---------------------------------------------*/
		public Hour(Int64 h = 0, Int64 m = 0, Int64 s = 0)
			{
			Hour.m_cptClassInstance++;
			m_hashcode = Hour.m_cptClassInstance;
			m_h = h;
			m_m = m;
			m_s = s;
			this.check();
			}

		/*---------------------------------------------*\
		|*               Methodes public               *|
		\*---------------------------------------------*/
		public void display()
			{
			System.Console.WriteLine("{0:0#}:{1:0#}:{2:0#}", m_h, m_m, m_s);
			}

		public override bool Equals(Object o)
			{
			if(o is Hour)
				{
				Hour hour = (Hour)o;
				return (m_h == hour.h && m_m == hour.m && m_s == hour.s) ? true : false;
				}

			return false;
			}

		public int compareTo(Object o)
			{
			if(o is Hour)
				{
				Hour other = (Hour)o;

				if(this < other)
					{
					return -1;
					}
				else if(this == other)
					{
					return 0;
					}
				else
					{
					return 1;
					}
				}
			else
				{
				throw new ArgumentException("Object is not a temperature");
				}
			}

		/// <summary>
		/// Doit être surchargé si Equals est utilisé. Cette méthode doit retourner un nombre unique pour le hashage.
		/// Ici c'est le numéro de l'instance qui est retourné. Chaque instance possède un attribut qui prend une valeur
		/// correspondant à un numéro d'instance unique.
		/// </summary>
		/// <returns></returns>
		public override int GetHashCode()
			{
			return m_hashcode;
			}

		public override string ToString()
			{
			StringBuilder str = new StringBuilder();
			str.AppendFormat("{0:0#}:{1:0#}:{2:0#}", m_h, m_m, m_s);
			return str.ToString();
			}

		public void add(Hour hour)
			{
			this.m_h += hour.h;
			this.m_m += hour.m;
			this.m_s += hour.s;
			this.check();
			}

		public void sub(Hour hour)
			{
			this.m_h -= hour.h;
			this.m_m -= hour.m;
			this.m_s -= hour.s;
			this.check();
			}

		public void adjust(Int32 h, Int32 m, Int32 s)
			{
			this.m_h = h;
			this.m_m = m;
			this.m_s = s;
			this.check();
			}

		public void incrementMinutes(Int32 m)
			{
			this.m_m += m;
			this.check();
			}

		public void incrementSeconds(Int32 s)
			{
			this.m_s += s;
			this.check();
			}

		public Int64 h
			{
			get { return this.m_h; }
			}

		public Int64 m
			{
			get { return this.m_m; }
			}

		public Int64 s
			{
			get { return this.m_s; }
			}

		/*---------------------*\
		|*        static       *|
		\*---------------------*/
		public static implicit operator string(Hour h1)
			{
			return h1.ToString();
			}

		public static explicit operator long(Hour h1)
			{
			long seconds = h1.m_s; //récupération des secondes
			seconds += h1.m_m * 60;//calcul des minutes en secondes
			seconds += h1.m_h * 60 * 60; //calcul des heures en secondes
			return seconds;
			}

		public static explicit operator Hour(long s)
			{
			Hour h = new Hour(0, 0, s);
			return h;
			}

		public static explicit operator double(Hour h1)
			{
			long seconds = (long)h1;
			return (double)seconds / (double)3600;
			}

		public static explicit operator Hour(double h)
			{
			return new Hour(0, 0, (long)(h * 3600));
			}

		public static Hour operator +(Hour h1, Hour h2)
			{
			Hour h3 = new Hour(h1.h, h1.m, h1.s);
			h3.add(h2);
			return h3;
			}

		public static Hour operator -(Hour h1, Hour h2)
			{
			Hour h3 = new Hour(h1.h, h1.m, h1.s);
			h3.sub(h2);
			return h3;
			}

		public static bool operator ==(Hour h1, Hour h2)
			{
			return h1.Equals(h2);
			}

		public static bool operator ==(Hour h1, double h2)
			{
			return h1.Equals((Hour)h2);
			}

		public static bool operator ==(double h1, Hour h2)
			{
			return ((Hour)h1).Equals(h2);
			}

		public static bool operator !=(Hour h1, Hour h2)
			{
			return !(h1 == h2);
			}

		public static bool operator !=(Hour h1, double h2)
			{
			return !(h1 == (Hour)h2);
			}

		public static bool operator !=(double h1, Hour h2)
			{
			return !((Hour)h1 == h2);
			}

		public static bool operator <(Hour h1, Hour h2)
			{
			Int64 s1 = (Int64)h1;
			Int64 s2 = (Int64)h2;
			return s1 < s2;
			}

		public static bool operator <(Hour h1, double h2)
			{
			Int64 s1 = (Int64)h1;
			Int64 s2 = (Int64)((Hour)h2);
			return s1 < s2;
			}

		public static bool operator <(double h1, Hour h2)
			{
			Int64 s2 = (Int64)h2;
			Int64 s1 = (Int64)((Hour)h1);
			return s1 < s2;
			}

		public static bool operator >(Hour h1, Hour h2)
			{
			return !(h1 <= h2);
			}

		public static bool operator >(Hour h1, double h2)
			{
			return !(h1 <= (Hour)h2);
			}

		public static bool operator >(double h1, Hour h2)
			{
			return !((Hour)h1 <= h2);
			}

		public static bool operator >=(Hour h1, Hour h2)
			{
			return !(h1 < h2);
			}

		public static bool operator >=(Hour h1, double h2)
			{
			return !(h1 < (Hour)h2);
			}

		public static bool operator >=(double h1, Hour h2)
			{
			return !((Hour)h1 < h2);
			}

		public static bool operator <=(Hour h1, Hour h2)
			{
			return !(h2 < h1);
			}

		public static bool operator <=(Hour h1, double h2)
			{
			return !((Hour)h2 < h1);
			}

		public static bool operator <=(double h1, Hour h2)
			{
			return !(h2 < (Hour)h1);
			}

		/*---------------------------------------------*\
		|*              Methodes private               *|
		\*---------------------------------------------*/
		/// <summary>
		/// Cette méthode permet de corriger les valeurs internes de l'heure pour avoir une heure cohérente.
		/// Elle doit être appelée à chaque fois que les attributs changent
		/// </summary>
		private void check()
			{
			if(m_s >= 60 || m_s < 0)
				{
				Int32 ts = (Int32)(m_s % 60);
				Int32 t = (Int32)(m_s / 60);

				if(m_s < 0)
					{
					ts = 60 + ts;
					t -= 1;
					}

				m_s = ts;
				m_m += t;
				}


			if(m_m >= 60 || m_m < 0)
				{
				Int32 ts = (Int32)(m_m % 60);
				Int32 t = (Int32)(m_m / 60);

				if(m_m < 0)
					{
					ts = 60 + ts;
					t -= 1;
					}

				m_m = ts;
				m_h += t;
				}

			if(m_h >= 24)
				{
				m_h = (Int32)(m_h % 24);
				}
			else if(m_h < 0)
				{
				m_h = (Int32)(m_h % 24);
				m_h = 24 + m_h;
				}
			}
		}
	}
